#pragma once

#include <cassert>
#include "color.h"

namespace img {

class stat_calculator
{
public:
	stat_calculator();
	void clear();
	void add(BGR<unsigned char> const & c);
	void add(stat_calculator const & r);
	void remove(BGR<unsigned char> const & c);
	void swap(stat_calculator & r);
	BGR<float> mean() const;
	float dispersion() const;
	unsigned count() const;
private:
	BGR<unsigned> sum_color;
	long long unsigned sum_squared_color;
	unsigned n;
};

//inline implementation

inline stat_calculator::stat_calculator()
{
	clear();
}

inline void stat_calculator::clear()
{
	sum_color = BGR<unsigned>(0,0,0);
	sum_squared_color = 0;
	n = 0;
}

inline void stat_calculator::add(BGR<unsigned char> const & c)
{
	sum_color += c;
	sum_squared_color += c.sqr<unsigned>();
	++n;
}

inline void stat_calculator::add(stat_calculator const & r)
{
	sum_color += r.sum_color;
	sum_squared_color += r.sum_squared_color;
	n += r.n;
}

inline void stat_calculator::remove(BGR<unsigned char> const & c)
{
	sum_color -= c;
	sum_squared_color -= c.sqr<unsigned>();
	--n;
}

inline void stat_calculator::swap(stat_calculator & r)
{
	std::swap(sum_color, r.sum_color);
	std::swap(sum_squared_color, r.sum_squared_color);
	std::swap(n, r.n);
}

inline BGR<float> stat_calculator::mean() const
{
	assert(n != 0);
	return BGR<float>(sum_color) * (1.0f / n);
}

inline float stat_calculator::dispersion() const
{
	assert(n != 0);
	if (n == 1)
		return 0;
	float d = 1.0f / (n-1) * 
		(sum_squared_color - 1.0 / n * sum_color.sqr<double>()); //double required for high precision
	//although even with double some rounding errors may occur
	return d > 0 ? d : 0;
}

inline unsigned stat_calculator::count() const
{
	return n;
}

} //namespace img
